﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SNFEGame.Util;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace SNFEGame.Menus
{
    
    /// <summary>
    /// A generic class for menu creation. Takes parameters for menu items, which contain names and delegate methods to be called when they are selected, and 
    /// for parameters for look and feel, such as font.
    /// </summary>
    public class Menu
    {
        private MenuItem[] menuItems;

        private Vector2[] positions;

        private float padding = 20f;
        private bool greyOut = true;

        // Selection Stuff
        private int selectedItem = 0;
        private Color unselectedColor = Color.White, selectedColor = Color.DodgerBlue;

        private bool keyPressed = false;

        // Change to menu font
        private SpriteFont menuFont = Storage.GetFont("Large");

        // Change to menu cursor
        private Texture2D cursor = Storage.whiteDot;

        private bool visible = false;

        public bool Visible
        {
            get { return visible; }
        }

        public bool GreyOut
        {
            set { greyOut = value; }
            get { return greyOut; }
        }

        // Animation
        private const float FADE_IN_TIME = .5f, FADE_OUT_TIME = .5f;
        private float alpha = 1f;
        private bool fadeIn = false, fadeOut = false;

        public Menu(MenuItem[] menuItems)
        {
            this.menuItems = menuItems;

            // Menu item position initialization
            positions = new Vector2[menuItems.Length];

            Vector2 maxDim = GetLargestStringDimensions(menuItems);
            float totalMenuHeight = maxDim.Y * menuItems.Length + padding * (menuItems.Length - 1);
            float initialGap = (MainGame.Height - totalMenuHeight) / 2;
            float spaceIncrement = totalMenuHeight / menuItems.Length;

            for (int i = 0; i < menuItems.Length; i++)
                positions[i] = new Vector2(MainGame.Width / 2 - maxDim.X / 2, initialGap + i * spaceIncrement);
        }

        public Menu(MenuItem[] menuItems, SpriteFont menuFont)
            : this(menuItems)
        {
            this.menuFont = menuFont;
        }

        public Menu(List<MenuItem> list) : this(toMenuItemArray(list)) { }

        public Menu(List<MenuItem> list, SpriteFont sf) : this(toMenuItemArray(list), sf) { }

        public Menu(string[] s) : this(toMenuItemArray(s)) { }

        public Menu(string[] s, SpriteFont menuFont) : this(toMenuItemArray(s), menuFont) { }

        protected static MenuItem[] toMenuItemArray(string[] s)
        {
            MenuItem[] m = new MenuItem[s.Length];

            for (int i = 0; i < s.Length; i++)
                m[i] = new MenuItem(s[i]);

            return m;
        }

        protected static MenuItem[] toMenuItemArray(List<MenuItem> l) 
        {
            MenuItem[] m = new MenuItem[l.Count];

            for (int i = 0; i < l.Count; i++)
                m[i] = l[i];

            return m;
        }

        public void Show()
        {
            alpha = 0f;
            visible = true;
            fadeIn = true;
        }

        public void Update(float deltaT)
        {
            #region Animation
            if (fadeIn)
            {
                alpha = Math.Min(1f, alpha + 1f / (FADE_IN_TIME / deltaT));
                fadeIn = alpha < 1f;
            }
            else if (fadeOut)
            {
                alpha = Math.Max(0f, alpha - 1f / (FADE_OUT_TIME / deltaT));
                fadeOut = alpha > 0f;
            }
            #endregion

            #region KeyInput
            else if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Up))
            {
                if (!keyPressed)
                {
                    selectedItem--;
                    if (selectedItem < 0)
                        selectedItem = menuItems.Length - 1;
                }

                keyPressed = true;
            }
            else if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Down))
            {
                if (!keyPressed)
                    selectedItem = (selectedItem + 1) % menuItems.Length;

                keyPressed = true;
            }
            else if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Enter))
            {
                if (!keyPressed)
                    menuItems[selectedItem].CallMethod();

                keyPressed = true;
            }
            else
            {
                keyPressed = false;
            }
            #endregion
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Begin();
            if (greyOut)
                spriteBatch.Draw(Storage.whiteDot, new Rectangle(0, 0, MainGame.Width, MainGame.Height), Color.Black * .5f);

            for (int i = 0; i < menuItems.Length; i++)
                spriteBatch.DrawString(menuFont, menuItems[i].Text, positions[i],
                    (i == selectedItem && !fadeIn ? selectedColor : unselectedColor) * alpha);
            spriteBatch.End();
        }

        private Vector2 GetLargestStringDimensions(MenuItem[] menuItems)
        {
            Vector2 dim = Vector2.Zero;

            foreach (MenuItem m in menuItems)
            {
                Vector2 temp = menuFont.MeasureString(m.Text);

                if (temp.X > dim.X)
                    dim.X = temp.X;

                if (temp.Y > dim.Y)
                    dim.Y = temp.Y;
            }

            return dim;
        }
    }
}
